<?php
/*
Copyright (C) 2009 de Flotte Maxence

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
?>
<?php
	/**
	* Generate SQL Writing Query (UPDATE and INSERT)
	* @TODO Make recursive need (for exemple need('user')->need('town')->need('name')) and the same for get (get('user')->get('town')->get('name') or get('user')->town->name)
	*/

class ZzSQLReadingQueryGenerator {
	
	private $id;
	private $domain;
	private $type;
	private $pk = array();
	private $_needs = array();
	
	private $needQuery = false;
	
	private $count = 0;
	private $nbResult = 0;
	private $_results = array();
	
	private $_tables = array();
	private $_fields = array();
	
	private $_clauses = array(); //
	private $_clausesValue = array(); //
	private $_orderCriterias = array(); //
	
	
 /**
	* Contructor
	* @param int $id the id of the row. It could be an int (if the primary key is a collumn named id) or an array : array('key1' => 'value1', 'key2' => 'value2', ...). It could also be null, or partial (for multiple keys) .
	* @param string $type the domain name
	* @return void
	*/
	public function __construct($type, $id = array()) {
		//2RM
		//if(empty($id))
		//	throw new ZzERROR($this,'id must be defined');

		$this->type = 'Dom'.$type;
		
		$domain = $this->type;
		try{
		$this->domain = new $domain();
		}
		catch(Exception $e) {
			echo $e;
		}
		
		foreach($this->domain as $fieldName => $field) {
			if($field->primary)
				$this->pk[] = $fieldName;
		}
		
		if(!is_array($id)) {
			$id = array('id' => $id);
		}
		$this->id = $id;
	}
 /**
	* Need a field
	* @param string $field name of the field we need
	*/
	public function need($field) {
		if(!is_array($field)) {
			$field = array($field);
		}
		foreach($field as $pty) {
			if(!isSet($this->domain->$pty))
				throw new ZzERROR($this,'unknow column "'.$pty.'"');
			if(!in_array($pty, $this->_needs)) {
				$this->_needs[] = $pty;
				$this->needQuery = true;
			}
		}
	}
 /**
	* Get a field value
	* @param string $field field name
	* @return domtype the value of the field
	*/
	public function get($field) {
		//Does the developer used the need methode ?
		$this->need($field);
		
		//Did the query is already excuted ?
		if($this->needQuery) {
			$this->_execQuery();
			}
			//print_r($this->_results);
		return $this->_results[$this->count]->$field;
	}
 /**
	* Test if there more results
	* @return bool True if there more results, false else.
	*/
	public function hasMoreResults() {
		if($this->needQuery)
			$this->_execQuery();
		if($this->count == $this->nbResults)
			return false;
		return true;
	}
 /**
	* Move to the next result
	* @return bool True if there a next result, false else.
	*/
	public function moveNextResult() {
		$this->count++;
		if($this->count == $this->nbResults) {
			return false;
		}
		return true;
	}
 /**
	* Move to the result n
	* @param int The number of the result (start by 0)
	* @return bool True if there result exist, false else.
	*/
	public function moveToResult($n) {
		if($n > $this->nbResults or !is_int($n)) {
			return false;
		}
		$this->count = $n;
		return true;
	}
 /**
	* Execute the query
	* @return bool true if all is ok, else false
	*/
	public function _execQuery() {
		//Construct the query
			$_tables = array(); //Table list
			$_rows = array(); //Row list

		foreach($this->_needs as $field) {
			$dom = '`'.$GLOBALS['prefix'].$this->domain->$field->dom.'`';
			$row = $this->domain->$field->name;
			if(!in_array($dom,$_tables)) {
				$_tables[] = $dom;
				if(count($_tables) > 1) {
					foreach($this->pk as $pk) {
						$this->_clauses[] = $dom.'.`'.$pk.'` = '.$_tables[0].'.`'.$pk.'`';
					}
				}
			}
			$_rows[] = $dom.'.`'.$row.'`';
		}
		foreach($this->id as $key => $val) {
			$this->_clauses[] = $_tables[0].'.`'.$key.'` = ?';
			$this->_clausesValue[] = $val;
		}
		
	foreach($this->_clauses as $key => $val)
		if(is_array($val)) {
			$this->_clauses[$key] = '('.implode(' OR ',$val).')';
		}
		
		$query = 'SELECT '.implode(', ',$_rows).' FROM '.str_repeat('(',count($_tables)-1).implode(') INNER JOIN ',$_tables).((count($this->_clauses) > 0)?(' WHERE '.implode(' AND ',$this->_clauses)):'').((count($this->_orderCriterias)>0)?' ORDER BY '.implode(',',$this->_orderCriterias):'');

		$_datas = ZzSQL::query($query,$this->_clausesValue);

		$this->nbResults = 0;

		foreach($_datas as $data) {
			$this->nbResults++;
			$row = new $this->type();
			$this->_results[] = $row;
			foreach($this->_needs as $field) {
				$row->$field->value = $data[$this->domain->$field->name];
			}
		}

		$this->needQuery = false;
		return true;
	}
 /**
	 * Criteria definition
	 * If we put more than one field (by using array), they will sépareted by OR operator
	 * @param string|array $field field or fields to filter
	 * @param string|array $operator operator(s) (<, >, <=, >=, =, <>)
	 * @param string|array $value comparaison value(s)
	 * @return void
	 */
	public function defCriteria($field,$operator,$value) {
		
		//Autocomplete params
			$max = max(count($field),count($operator),count($value));
			if(!is_array($field))
				$field = array_fill(0, $max, $field);
			if(!is_array($operator))
				$operator = array_fill(0, $max, $operator);	
			if(!is_array($value))
				$value = array_fill(0, $max, $value);

		$_clauses = array();
			
			if(!is_array($operator))
				$operator = array($operator);
				
			if(!is_array($value))
				$value = array($value);
		
		foreach($field as $key => $pty) {
			if(!isSet($this->domain->$pty))
				throw new ZzERROR($this,'unknow column "'.$pty.'"');

			//If it's a reference
			if(strToLower(get_parent_class($thid->domain->$pty)) == 'domref') {
				$_clauses[] = '`'.$GLOBALS['prefix'].$this->domain->$pty->name.'_'.$this->domain->$pty->dom.'`.`'.$this->domain->$pty->user.'` = ?';
				$this->_clausesValue[] = $_SESSION['user'];
			}
			
			$_clauses[] = '`'.$GLOBALS['prefix'].$this->domain->$pty->dom.'`.`'.$this->domain->$pty->name.'` '.$operator[$key].' ?';
			$this->_clausesValue[] = $value[$key];
		}
		$this->_clauses[] = $_clauses;
	}
 /**
	 * Criteria Order definition
	 * @param string|array $field this field will be used to order the response
	 * @return void
	 */
	public function addCriteriaOrder($field) {
		if(!is_array($field))
			$field = array($field);
			
		foreach($field as $pty) {
			if(empty($this->domain->$pty))
				throw new ZzERROR($this,'unknow column "'.$pty.'"');
			$this->_orderCriterias[] = '`'.$GLOBALS['prefix'].$this->domain->$pty->dom.'`.`'.$pty.'`';
		}
	}
}
?>